<?php

namespace paper;

use JetBrains\PhpStorm\NoReturn;
use paper\component\ExceptionCatcher;
use paper\component\RouteParser;
use paper\component\Session;
use paper\constraint\Bootstrap;
use paper\exception\ErrorException;
use paper\exception\ResponseException;
use paper\http\MiddlewareManager;
use paper\http\Request;
use paper\http\Router;
use Throwable;




class App
{
    public string $root;
    public string $appPath;
    public string $appNamespace;
    public string $corePath;
    public string $rootPath;

    public Config $config;
    public Request $request;
    public Session $session;
    public Router $router;
    private array $systemBootstraps = [

    ];


    /**
     * @param string $appPath
     */
    public function setAppPath(string $appPath): void
    {
        $this->appPath = $appPath;
    }

    /**
     * @param string $appNamespace
     */
    public function setAppNamespace(string $appNamespace): void
    {
        $this->appNamespace = $appNamespace;
    }

    public function __construct(string $app = "", string $namespace = null)
    {

        $this->rootPath = PAPER_ROOT_PATH;
        $this->appPath = PAPER_APP_PATH;
        $this->corePath = PAPER_CORE_PATH;

        $this->root = PAPER_ROOT_PATH;

        $this->appNamespace = PAPER_APP;

    }

    private function loadBootstrapComponents(array $bootstraps)
    {
        foreach ($bootstraps as $bootstrap) {
            $class = $bootstrap['class'];
            $instance = new $class();
            if ($instance instanceof Bootstrap) {
                $instance->onBoot($this);
            }
        }
    }

    #[NoReturn]
    public function exceptionCatcher(Throwable $throwable)
    {
        $exceptionCatcherClass = $this->config->get('exception_catcher', ExceptionCatcher::class);
        /**
         * @var $exceptionCatcher ExceptionCatcher
         */
        $exceptionCatcher = new $exceptionCatcherClass();
        $response = $exceptionCatcher->onCatch($throwable);
        $response->send();
        exit;
    }

    public function singleton(string $key, $instantiation)
    {
        Container::getInstance()->singleton($key, $instantiation);
    }

    public function startup()
    {
        //异常处理
        //  (new \paper\component\Exception(new ExceptionHandlerProvider()))->register();

        try {

            //将本实例添加到容器中
            Container::getInstance()->put(App::class, $this);

            Config::$configBasePath = $this->appPath . '/config';
            $this->config = Config::load('app');


            set_error_handler(function ($errno, $errstr, $errfile, $errline) {
                $level = ob_get_level();
                for ($i = 0; $i < $level; $i++)
                    ob_end_clean();
                $this->exceptionCatcher(new ErrorException($errno, $errstr, $errfile, $errline));
            });

            $this->request = Container::getInstance()->get(Request::class);


            $routeParserClass = $this->config->get('route_parser', RouteParser::class);
            $routeParser = new $routeParserClass();
            $middlewareManager = new MiddlewareManager();
            $this->router = new Router($routeParser, $middlewareManager);

            //加载启动组件
            $this->loadBootstrapComponents($this->systemBootstraps);
            $this->loadBootstrapComponents($this->config['bootstraps'] ?? []);

            $response = $this->router->dispatch($this->request);

            //构建请求
            $response->send();

        } catch (Throwable $throwable) {
            $level = ob_get_level();
            for ($i = 0; $i < $level; $i++)
                ob_end_clean();
            if ($throwable instanceof ResponseException) {
                $response = $throwable->getResponse();
                $response->send();
            } else {
                $this->exceptionCatcher($throwable);
            }
        }
    }
}